3f8bcf29ulZIC9rC4wM70H_q4s6VPg tools/misc/xen_log.c
3f13d81eQ9Vz-h-6RDGFkNR9CRP95g tools/misc/xen_nat_enable
3f13d81e6Z6806ihYYUw8GVKNkYnuw tools/misc/xen_nat_enable.README
-3f1668d4F29Jsw0aC0bJEIkOBiagiQ tools/misc/xen_read_console.c
4022a73cEKvrYe_DVZW2JlAxobg9wg tools/nsplitd/Makefile
4022a73cKms4Oq030x2JBzUB426lAQ tools/nsplitd/nsplitd.c
3fbca441SjQr8vJwTQIgH1laysaWog tools/xc/Makefile
3e5a4e66rw65CxyolW9PKz4GG42RcA xenolinux-2.4.25-sparse/drivers/char/tty_io.c
3e5a4e669uzIE54VwucPYtGwXLAbzA xenolinux-2.4.25-sparse/fs/exec.c
3e5a4e66wbeCpsJgVf_U8Jde-CNcsA xenolinux-2.4.25-sparse/include/asm-xeno/bugs.h
+4048c0ddxnIa2GpBAVR-mY6mNSdeJg xenolinux-2.4.25-sparse/include/asm-xeno/control_if.h
3e5a4e66HdSkvIV6SJ1evG_xmTmXHA xenolinux-2.4.25-sparse/include/asm-xeno/desc.h
+4048c0e0_P2wUTiT6UqgPhn0s7yFcA xenolinux-2.4.25-sparse/include/asm-xeno/evtchn.h
3e5a4e66SYp_UpAVcF8Lc1wa3Qtgzw xenolinux-2.4.25-sparse/include/asm-xeno/fixmap.h
3e5a4e67w_DWgjIJ17Tlossu1LGujQ xenolinux-2.4.25-sparse/include/asm-xeno/highmem.h
3e5a4e67YtcyDLQsShhCfQwPSELfvA xenolinux-2.4.25-sparse/include/asm-xeno/hw_irq.h
xc.domain_destroy ( dom=id )
sys.exit()
+ ports = xc.evtchn_open( dom2=id )
+ if not ports:
+ print "Error creating initial event channel"
+ xc.domain_destroy ( dom=id )
+ sys.exit()
+
# setup the virtual block devices
# set the expertise level appropriately
xc.domain_destroy ( dom=id )
sys.exit()
- return id
+ return (id, 9600+ports['port1'])
# end of make_domain()
def mkpidfile():
# The starting / monitoring of the domain actually happens here...
# start the domain and record its ID number
-current_id = make_domain()
-output("VM started in domain %d" % current_id)
+(current_id, current_port) = make_domain()
+output("VM started in domain %d. Console I/O available on TCP port %d." % (current_id,current_port))
# if the auto_restart flag is set then keep polling to see if the domain is
# alive - restart if it is not by calling make_domain() again (it's necessary
output("Auto-restart daemon: Domain %d has terminated, restarting VM in new domain"
% current_id)
rmpidfile()
- current_id = make_domain()
+ (current_id, current_port) = make_domain()
mkpidfile()
- output("Auto-restart daemon: VM restarted in domain %d" % current_id)
+ output("Auto-restart daemon: VM restarted in domain %d. Console on port %d." % (current_id,current_port))
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
-TARGETS = xen_read_console xen_cpuperf
+TARGETS = xen_cpuperf
INSTALL = $(TARGETS) xen-mkdevnodes xen_nat_enable xen-clone
+++ /dev/null
-/******************************************************************************
- * Test program for reading console lines from DOM0 port 666.
- */
-
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-int main(void)
-{
- unsigned char buf[208], filtered[208];
- struct sockaddr_in addr, from;
- int fromlen = sizeof(from), i, j;
- int len, fd = socket(PF_INET, SOCK_DGRAM, 0);
-
- if ( fd < 0 )
- {
- fprintf(stderr, "could not open datagram socket\n");
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_addr.s_addr = htonl(0xa9fe0100); /* 169.254.1.0 */
- addr.sin_port = htons(666);
- addr.sin_family = AF_INET;
- if ( bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 )
- {
- fprintf(stderr, "could not bind to local address and port\n");
- return -1;
- }
-
- while ( (len = recvfrom(fd, buf, sizeof(buf), 0,
- (struct sockaddr *)&from, &fromlen))
- >= 0 )
- {
-#if 0
- unsigned char abuf[32];
- printf("%d-byte message from %s:%d --\n", len,
- inet_ntop(AF_INET, &from.sin_addr, abuf, sizeof(abuf)),
- ntohs(from.sin_port));
-#endif
- /* For sanity, clean up the string's tail. */
- if ( buf[len-1] != '\n' ) { buf[len] = '\n'; len++; }
- buf[len] = '\0';
-
- for ( i = 0, j = 0; i < len; i++ )
- if ( (buf[i] == '\n') || (buf[i] == '\0') ||
- ((buf[i] >= 32) && (buf[i] <= 126)) )
- filtered[j++] = buf[i];
-
- printf("[%d] %s", ntohs(from.sin_port), filtered);
-
- fromlen = sizeof(from);
- }
-
- return 0;
-}
goto error_out;
}
- if ( ramdisk_name != NULL )
+ if ( (ramdisk_name != NULL) && (strlen(ramdisk_name) != 0) )
{
initrd_fd = open(ramdisk_name, O_RDONLY);
if ( initrd_fd < 0 )
CC = gcc
CFLAGS = -Wall -O3
-CFLAGS += -I../xc/lib
+CFLAGS += -I../xc/lib -I../../xenolinux-sparse/include
HDRS = $(wildcard *.h)
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/mman.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <signal.h>
#include <xc.h>
+#include <asm-xeno/control_if.h>
/* NB. The following should be kept in sync with the kernel's evtchn driver. */
#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
#define DOM0_HINT() HINT("You must execute this daemon " \
"on a privileged Xenolinux instance (e.g., DOM0).")
-/* The following is to be shared with guest kernels. */
-typedef struct {
- u8 cmd_type; /* echoed in response */
- u8 cmd_subtype; /* echoed in response */
- u8 id; /* echoed in response */
- u8 length; /* number of bytes in 'msg' */
- unsigned char msg[60]; /* command-specific message data */
-} control_msg_t;
-#define CONTROL_RING_SIZE 8
-typedef unsigned int CONTROL_RING_IDX;
-#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1))
-typedef struct {
- control_msg_t tx_ring[CONTROL_RING_SIZE]; /* guest -> DOM0 */
- control_msg_t rx_ring[CONTROL_RING_SIZE]; /* DOM0 -> guest */
- CONTROL_RING_IDX tx_req_prod, tx_resp_prod;
- CONTROL_RING_IDX rx_req_prod, rx_resp_prod;
-} control_comms_t;
-#define CMD_CONSOLE 0
-#define CMD_CONSOLE_DATA 0
-
-#define PAGE_SHIFT 12
-#define PAGE_SIZE (1<<PAGE_SHIFT)
-
-typedef struct {
+#if 0
+#define DPRINTF(_f, _a...) \
+ fprintf ( stdout, _f "\n" , ## _a );
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+/* Per-port Tx/Rx buffering. */
+#define CONBUFSZ 65536
+#define MASK_CONBUF_IDX(_i) ((_i)&(CONBUFSZ-1))
+
+struct portinfo;
+typedef struct portinfo {
u64 dom;
- control_comms_t *comms;
+ control_if_t *interface;
CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
- CONTROL_RING_IDX rx_req_cons, rx_resp_prod;
+ CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
+ char *tx_buf, *rx_buf;
+ unsigned int txp, txc, rxp, rxc;
+#define CONSTAT_CLOSED 0
+#define CONSTAT_LISTENING 1
+#define CONSTAT_CONNECTED 2
+ int con_fd, con_status;
+ struct portinfo **pprev, *next; /* links to other active ports */
} portinfo_t;
-static portinfo_t portinfo[1024];
+#define PORT(_pinfo) ((_pinfo)-portinfo)
+#define TX_EMPTY(_pinfo) ((_pinfo)->txp == (_pinfo)->txc)
+#define TX_FULL(_pinfo) (((_pinfo)->txp - (_pinfo)->txc) == CONBUFSZ)
+#define RX_EMPTY(_pinfo) ((_pinfo)->rxp == (_pinfo)->rxc)
+#define RX_FULL(_pinfo) (((_pinfo)->rxp - (_pinfo)->rxc) == CONBUFSZ)
+
+static portinfo_t *active_head; /* linked list of active ports */
+static portinfo_t portinfo[1024]; /* array of all ports */
+static int xc_fd, evt_fd, mem_fd;
+
+#define PAGE_SIZE 4096 /* size of a machine page frame */
+#define BATCH_SIZE 512 /* maximum notifications to read at a time */
+
+static int make_consock_listener(portinfo_t *pinfo);
+static int make_consock_connected(portinfo_t *pinfo);
+static void make_consock_closed(portinfo_t *pinfo);
+static void do_consock_read(portinfo_t *pinfo);
+static void do_consock_write(portinfo_t *pinfo);
+static int process_evtchn_reads(portinfo_t *pinfo);
+static int process_evtchn_writes(portinfo_t *pinfo);
-static control_comms_t *map_comms(int fd, unsigned long pfn)
+static control_if_t *map_control_interface(int fd, unsigned long pfn)
{
char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_SHARED, fd, pfn << PAGE_SHIFT);
+ MAP_SHARED, fd, pfn * PAGE_SIZE);
if ( vaddr == MAP_FAILED )
return NULL;
- return (control_comms_t *)(vaddr + 2048);
+ return (control_if_t *)(vaddr + 2048);
}
-static void unmap_comms(int fd, control_comms_t *c)
+static void unmap_control_interface(int fd, control_if_t *c)
{
char *vaddr = (char *)c - 2048;
(void)munmap(vaddr, PAGE_SIZE);
}
-#define PORT_CHUNK 4
+/* Returns TRUE if the channel is open on exit. */
+static int handle_channel_exception(unsigned int port)
+{
+ xc_dominfo_t info;
+ unsigned int remote_port, status;
+ u64 remote_dom;
+ u16 wbuf;
+ portinfo_t *pinfo = &portinfo[port];
+
+ if ( xc_evtchn_status(xc_fd, DOMID_SELF, port,
+ &remote_dom, &remote_port, &status) != 0 )
+ {
+ SYS_ERROR("Unexpected failure when obtaining port-%d status.", port);
+ exit(1);
+ }
+
+ if ( status != EVTCHNSTAT_connected )
+ {
+ DPRINTF("Port %d not connected: cleaning up.", port);
+ if ( pinfo->interface != NULL )
+ {
+ unmap_control_interface(mem_fd, pinfo->interface);
+ pinfo->interface = NULL;
+ *(pinfo->pprev) = pinfo->next;
+ if ( pinfo->next != NULL )
+ pinfo->next->pprev = pinfo->pprev;
+ make_consock_closed(pinfo);
+ free(pinfo->tx_buf);
+ free(pinfo->rx_buf);
+ memset(pinfo, 0, sizeof(*pinfo));
+ }
+ /* Cleanup sanity: we'll be the grim reaper. */
+ wbuf = port | PORT_NORMAL;
+ (void)write(evt_fd, &wbuf, sizeof(wbuf));
+ wbuf = port | PORT_DISCONNECT;
+ (void)write(evt_fd, &wbuf, sizeof(wbuf));
+ if ( status == EVTCHNSTAT_disconnected )
+ (void)xc_evtchn_close(xc_fd, DOMID_SELF, port);
+ return 0;
+ }
+
+ /* We only deal with initial ports (id == 0). */
+ if ( remote_port != 0 )
+ return 0;
+
+ if ( pinfo->interface == NULL )
+ {
+ DPRINTF("New control interface for DOM%llu on port %d.",
+ remote_dom, port);
+ if ( xc_domain_getinfo(xc_fd, remote_dom, 1, &info) != 1 )
+ {
+ SYS_ERROR("Failed to obtain DOM%llu status.", remote_dom);
+ exit(1);
+ }
+ memset(pinfo, 0, sizeof(*pinfo));
+ pinfo->interface =
+ map_control_interface(mem_fd, info.shared_info_frame);
+ pinfo->tx_buf = malloc(CONBUFSZ);
+ pinfo->rx_buf = malloc(CONBUFSZ);
+ pinfo->dom = remote_dom;
+ pinfo->con_status = CONSTAT_CLOSED;
+ if ( !make_consock_listener(pinfo) )
+ {
+ ERROR("Could not start console %d in listener status.",
+ PORT(pinfo));
+ exit(1);
+ }
+ pinfo->pprev = &active_head;
+ if ( (pinfo->next = active_head) != NULL )
+ pinfo->next->pprev = &pinfo->next;
+ active_head = pinfo;
+ }
+
+ return 1;
+}
+
+static void process_channel(unsigned int port)
+{
+ portinfo_t *pinfo = &portinfo[port];
+ u16 wbuf = port;
+
+ /* Acknowledge the notification. */
+ (void)write(evt_fd, &wbuf, sizeof(wbuf));
+
+ /* Process requests; send notification if we updated either ring. */
+ if ( process_evtchn_reads(pinfo) || process_evtchn_writes(pinfo) )
+ (void)xc_evtchn_send(xc_fd, port);
+}
+
int main(int argc, char **argv)
{
- int fd, memfd, xch, chunk;
- unsigned int bytes, i, port, portid, status;
- u64 domid;
- u16 buf[PORT_CHUNK];
+ struct pollfd polls[1025]; /* one per port, plus /dev/xeno/evtchn */
+ portinfo_t *pinfo;
+ unsigned int batch, bytes, i, port, fd_idx;
+ u16 buf[BATCH_SIZE];
- if ( (fd = open(EVTCHN_DEV_NAME, O_RDWR)) == -1 )
+ /* Ignore writes to disconnected sockets. We clear up later. */
+ (void)signal(SIGPIPE, SIG_IGN);
+
+ if ( (evt_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR)) == -1 )
{
SYS_ERROR("Could not open '%s'", EVTCHN_DEV_NAME);
ROOT_HINT();
exit(1);
}
- if ( (memfd = open("/dev/mem", O_RDWR)) == -1 )
+ if ( (mem_fd = open("/dev/mem", O_RDWR)) == -1 )
{
SYS_ERROR("Could not open '/dev/mem'");
ROOT_HINT();
exit(1);
}
- if ( (xch = xc_interface_open()) == -1 )
+ if ( (xc_fd = xc_interface_open()) == -1 )
{
SYS_ERROR("Could not open Xen control interface");
ROOT_HINT();
exit(1);
}
- while ( (bytes = read(fd, buf, sizeof(buf))) == -1 )
+ for ( ; ; )
{
- if ( errno == EINTR )
+ polls[0].fd = evt_fd;
+ polls[0].events = POLLIN;
+
+ fd_idx = 1;
+ for ( pinfo = active_head; pinfo != NULL; pinfo = pinfo->next )
+ {
+ switch ( pinfo->con_status )
+ {
+ case CONSTAT_LISTENING:
+ polls[fd_idx].fd = pinfo->con_fd;
+ polls[fd_idx].events = POLLIN;
+ fd_idx++;
+ break;
+ case CONSTAT_CONNECTED:
+ polls[fd_idx].fd = pinfo->con_fd;
+ polls[fd_idx].events = POLLIN | (RX_EMPTY(pinfo)?0:POLLOUT);
+ fd_idx++;
+ break;
+ }
+ }
+
+ while ( poll(polls, fd_idx, -1) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ SYS_ERROR("Unexpected error from poll().");
+ exit(1);
+ }
+
+ fd_idx = 1;
+ for ( pinfo = active_head; pinfo != NULL; pinfo = pinfo->next )
+ {
+ switch ( pinfo->con_status )
+ {
+ case CONSTAT_LISTENING:
+ if ( ((polls[fd_idx].revents & POLLIN) != 0) )
+ (void)make_consock_connected(pinfo);
+ break;
+ case CONSTAT_CONNECTED:
+ if ( ((polls[fd_idx].revents & POLLOUT) != 0) )
+ do_consock_write(pinfo);
+ if ( ((polls[fd_idx].revents & POLLIN) != 0) )
+ do_consock_read(pinfo);
+ break;
+ }
+ fd_idx++;
+ }
+
+ while ( (bytes = read(evt_fd, buf, sizeof(buf))) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ if ( errno == EAGAIN )
+ {
+ bytes = 0;
+ break;
+ }
+ SYS_ERROR("Unexpected error while reading '%s'.", EVTCHN_DEV_NAME);
+ exit(1);
+ }
+
+ if ( bytes == 0 )
continue;
- SYS_ERROR("Unexpected error reading '%s'.", EVTCHN_DEV_NAME);
- exit(1);
+
+ if ( (bytes & 1) != 0 )
+ {
+ ERROR("Bad read length (%d bytes) from '%s'.",
+ bytes, EVTCHN_DEV_NAME);
+ exit(1);
+ }
+
+ batch = bytes / sizeof(u16);
+ for ( i = 0; i < batch; i++ )
+ {
+ port = buf[i] & PORTIDX_MASK;
+
+ if ( buf[i] & PORT_DISCONNECT )
+ {
+ DPRINTF("Disconnect on port %d.", port);
+ (void)handle_channel_exception(port);
+ continue;
+ }
+
+ if ( portinfo[port].interface == NULL )
+ {
+ DPRINTF("Unexpected notification on port %d.", port);
+ if ( !handle_channel_exception(port) )
+ continue;
+ }
+
+ process_channel(port);
+ }
+ }
+
+ (void)xc_interface_close(xc_fd);
+ (void)close(mem_fd);
+ (void)close(evt_fd);
+
+ return 0;
+}
+
+
+/* Returns non-zero if console is listening on exit. */
+static int make_consock_listener(portinfo_t *pinfo)
+{
+ int reuseaddr_flag = 1;
+ struct linger linger;
+ int tcp_port = 9600 + PORT(pinfo);
+ int fd, flags;
+ struct sockaddr_in sa;
+
+ if ( pinfo->con_status == CONSTAT_LISTENING )
+ return 1;
+
+ if ( pinfo->con_status == CONSTAT_CONNECTED )
+ {
+ (void)close(pinfo->con_fd);
+ pinfo->con_status = CONSTAT_CLOSED;
}
- if ( (bytes == 0) || ((bytes & 1) != 0) )
+ if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
- ERROR("Short or bad read length (%d bytes) from '%s'.",
- bytes, EVTCHN_DEV_NAME);
- exit(1);
+ SYS_ERROR("Could not create TCP socket.");
+ return 0;
+ }
+
+ linger.l_onoff = 0;
+ if ( (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ &reuseaddr_flag, sizeof(int)) != 0) ||
+ (setsockopt(fd, SOL_SOCKET, SO_LINGER,
+ &linger, sizeof(linger)) != 0) )
+ {
+ SYS_ERROR("Could not enable immediate reuse of socket port.");
+ close(fd);
+ return 0;
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+ sa.sin_port = htons(tcp_port);
+ if ( bind(fd, (struct sockaddr *)&sa, sizeof(sa)) != 0 )
+ {
+ SYS_ERROR("Unable to bind to console port %d.", tcp_port);
+ close(fd);
+ return 0;
+ }
+
+ if ( listen(fd, 5) != 0 )
+ {
+ SYS_ERROR("Unable to listen on console port %d.", tcp_port);
+ close(fd);
+ return 0;
+ }
+
+ if ( ((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
+ (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) )
+ {
+ SYS_ERROR("Unable to set non-blocking status for console listener.");
+ close(fd);
+ return 0;
+ }
+
+ pinfo->con_fd = fd;
+ pinfo->con_status = CONSTAT_LISTENING;
+ return 1;
+}
+
+/* Returns non-zero if console is connected on exit. */
+static int make_consock_connected(portinfo_t *pinfo)
+{
+ int fd, flags, sa_len;
+ struct linger linger;
+ struct sockaddr_in sa;
+
+ if ( pinfo->con_status == CONSTAT_CONNECTED )
+ return 1;
+
+ if ( pinfo->con_status == CONSTAT_CLOSED )
+ return 0;
+
+ if ( (fd = accept(pinfo->con_fd, (struct sockaddr *)&sa, &sa_len)) == -1 )
+ return 0;
+
+ linger.l_onoff = 0;
+ if ( setsockopt(fd, SOL_SOCKET, SO_LINGER,
+ &linger, sizeof(linger)) != 0 )
+ {
+ SYS_ERROR("Could not enable immediate socket death.");
+ close(fd);
+ return 0;
}
- chunk = bytes / 2;
- for ( i = 0; i < chunk; i++ )
+ if ( ((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
+ (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) )
{
- port = buf[i] & PORTIDX_MASK;
- if ( buf[i] & PORT_DISCONNECT )
+ SYS_ERROR("Unable to set non-blocking status on socket.");
+ close(fd);
+ return 0;
+ }
+
+ (void)close(pinfo->con_fd);
+
+ pinfo->con_fd = fd;
+ pinfo->con_status = CONSTAT_CONNECTED;
+ return 1;
+}
+
+
+static void make_consock_closed(portinfo_t *pinfo)
+{
+ if ( pinfo->con_status != CONSTAT_CLOSED )
+ (void)close(pinfo->con_fd);
+ pinfo->con_status = CONSTAT_CLOSED;
+}
+
+
+static void do_consock_read(portinfo_t *pinfo)
+{
+ char buf[1024];
+ int idx, bytes, rc, was_empty = TX_EMPTY(pinfo);
+
+ while ( (rc = read(pinfo->con_fd, &buf, sizeof(buf))) > 0 )
+ {
+ idx = 0;
+ while ( (rc != 0) && !TX_FULL(pinfo) )
{
- if ( portinfo[port].comms == NULL )
- continue;
- unmap_comms(memfd, portinfo[port].comms);
- portinfo[port].comms = NULL;
- (void)write(fd, &buf[i], sizeof(u16));
- (void)xc_evtchn_close(xch, DOMID_SELF, port);
+ bytes = rc;
+ /* Clip copy to ring-buffer wrap. */
+ if ( bytes > (CONBUFSZ - MASK_CONBUF_IDX(pinfo->txp)) )
+ bytes = CONBUFSZ - MASK_CONBUF_IDX(pinfo->txp);
+ /* Clip copy to ring-buffer overflow. */
+ if ( bytes > (CONBUFSZ - (pinfo->txp - pinfo->txc)) )
+ bytes = CONBUFSZ - (pinfo->txp - pinfo->txc);
+ memcpy(&pinfo->tx_buf[MASK_CONBUF_IDX(pinfo->txp)],
+ &buf[idx], bytes);
+ pinfo->txp += bytes;
+ idx += bytes;
+ rc -= bytes;
}
- else
+ }
+
+ if ( (rc == 0) || (errno != EAGAIN) )
+ {
+ DPRINTF("Console client has disconnected.");
+ if ( !make_consock_listener(pinfo) )
{
- if ( portinfo[port].comms == NULL )
- {
- xc_dominfo_t info;
- xc_evtchn_status(xch, DOMID_SELF, port,
- &domid, &portid, &status);
-
- if ( (status == EVTCHNSTAT_closed) ||
- ((status == EVTCHNSTAT_disconnected) && (portid == 0)) )
- {
- /* Cleanup sanity: we'll be the grim reaper. */
- (void)write(fd, &buf[i], sizeof(u16)); /* PORT_NORMAL */
- buf[i] |= PORT_DISCONNECT;
- (void)write(fd, &buf[i], sizeof(u16)); /* PORT_DISCON */
- continue;
- }
+ ERROR("Could not revert console %d to listener status.",
+ PORT(pinfo));
+ exit(1);
+ }
+ }
- /* We only deal with initial ports (id == 0). */
- if ( portid != 0 )
- continue;
+ if ( was_empty && !TX_EMPTY(pinfo) )
+ {
+ /* There is now data to transmit to guest. Kickstart the pipeline. */
+ if ( process_evtchn_writes(pinfo) )
+ (void)xc_evtchn_send(xc_fd, PORT(pinfo));
+ }
+}
- xc_domain_getinfo(xch, domid, 1, &info);
- portinfo[port].comms =
- map_comms(memfd, info.shared_info_frame);
- portinfo[port].dom = domid;
- portinfo[port].tx_req_cons = 0;
- portinfo[port].tx_resp_prod = 0;
- portinfo[port].rx_req_cons = 0;
- portinfo[port].rx_resp_prod = 0;
- }
+static void do_consock_write(portinfo_t *pinfo)
+{
+ int bytes, rc;
+
+ while ( !RX_EMPTY(pinfo) )
+ {
+ /* Clip transfer to ring-buffer wrap. */
+ bytes = CONBUFSZ - MASK_CONBUF_IDX(pinfo->rxc);
+ /* Clip transfer to ring-buffer overflow. */
+ if ( bytes > (pinfo->rxp - pinfo->rxc) )
+ bytes = pinfo->rxp - pinfo->rxc;
+ rc = write(pinfo->con_fd,
+ &pinfo->rx_buf[MASK_CONBUF_IDX(pinfo->rxc)],
+ bytes);
+ if ( rc <= 0 )
+ return; /* Nothing to do. Errors cleaned up in reader code. */
+ pinfo->rxc += rc;
+ }
+}
+
+static int process_evtchn_reads(portinfo_t *pinfo)
+{
+ CONTROL_RING_IDX c;
+ control_if_t *cif = pinfo->interface;
+ control_msg_t *cmsg;
+ unsigned int clen, idx, len, bytes;
+
+ for ( c = pinfo->tx_req_cons;
+ (c != cif->tx_req_prod) &&
+ ((c-pinfo->tx_resp_prod) != CONTROL_RING_SIZE);
+ c++ )
+ {
+ cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
+
+ if ( (clen = cmsg->length) > sizeof(cmsg->msg) )
+ clen = sizeof(cmsg->msg);
- do {
- xc_evtchn_send(xch, port);
- write(fd, &buf[i], sizeof(u16));
- } while ( 0 );
+ if ( (cmsg->cmd_type == CMD_CONSOLE) &&
+ (cmsg->cmd_subtype == CMD_CONSOLE_DATA) )
+ {
+ idx = 0;
+ len = cmsg->length;
+ while ( (len != 0) && !RX_FULL(pinfo) )
+ {
+ bytes = len;
+ /* Clip copy to ring-buffer wrap. */
+ if ( bytes > (CONBUFSZ - MASK_CONBUF_IDX(pinfo->rxp)) )
+ bytes = CONBUFSZ - MASK_CONBUF_IDX(pinfo->rxp);
+ /* Clip copy to ring-buffer overflow. */
+ if ( bytes > (CONBUFSZ - (pinfo->rxp - pinfo->rxc)) )
+ bytes = CONBUFSZ - (pinfo->rxp - pinfo->rxc);
+ memcpy(&pinfo->rx_buf[MASK_CONBUF_IDX(pinfo->rxp)],
+ &cmsg->msg[idx], bytes);
+ pinfo->rxp += bytes;
+ idx += bytes;
+ len -= bytes;
+ }
}
+
+ /* Prepare response. No payload; msg type and id same as request. */
+ cmsg->length = 0;
+ }
+
+ if ( c != pinfo->tx_req_cons )
+ {
+ /* Update private indexes. */
+ pinfo->tx_resp_prod = c;
+ pinfo->tx_req_cons = c;
+ /* Queue responses and send a notification to the guest OS. */
+ cif->tx_resp_prod = c;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int process_evtchn_writes(portinfo_t *pinfo)
+{
+ CONTROL_RING_IDX p, rx_resp_prod;
+ control_if_t *cif = pinfo->interface;
+ control_msg_t *cmsg;
+ unsigned int bytes;
+
+ /* Validate the rx-response producer, an dupdate our consumer if okay. */
+ rx_resp_prod = cif->rx_resp_prod;
+ if ( (pinfo->rx_resp_cons != rx_resp_prod) &&
+ ((pinfo->rx_req_prod - rx_resp_prod) <= CONTROL_RING_SIZE) &&
+ ((rx_resp_prod - pinfo->rx_resp_cons) <= CONTROL_RING_SIZE) )
+ pinfo->rx_resp_cons = cif->rx_resp_prod;
+
+ for ( p = pinfo->rx_req_prod;
+ (p-pinfo->rx_resp_cons) != CONTROL_RING_SIZE;
+ p++ )
+ {
+ if ( TX_EMPTY(pinfo) )
+ break;
+ cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
+ bytes = sizeof(cmsg->msg);
+ /* Clip transfer to ring-buffer wrap. */
+ if ( bytes > (CONBUFSZ - MASK_CONBUF_IDX(pinfo->txc)) )
+ bytes = CONBUFSZ - MASK_CONBUF_IDX(pinfo->txc);
+ /* Clip transfer to ring-buffer overflow. */
+ if ( bytes > (pinfo->txp - pinfo->txc) )
+ bytes = pinfo->txp - pinfo->txc;
+ cmsg->cmd_type = CMD_CONSOLE;
+ cmsg->cmd_subtype = CMD_CONSOLE_DATA;
+ cmsg->id = 0xaa;
+ cmsg->length = bytes;
+ memcpy(&cmsg->msg[0],
+ &pinfo->tx_buf[MASK_CONBUF_IDX(pinfo->txc)],
+ bytes);
+ pinfo->txc += bytes;
}
- (void)xc_interface_close(xch);
- (void)close(memfd);
- (void)close(fd);
+ if ( p != pinfo->rx_req_prod )
+ {
+ pinfo->rx_req_prod = p;
+ cif->rx_req_prod = p;
+ return 1;
+ }
return 0;
}
put_page(page);
return ret;
}
+
+#ifdef MEMORY_GUARD
+
+void *memguard_init(void *heap_start)
+{
+ l1_pgentry_t *l1;
+ int i, j;
+
+ /* Round the allocation pointer up to a page boundary. */
+ heap_start = (void *)(((unsigned long)heap_start + (PAGE_SIZE-1)) &
+ PAGE_MASK);
+
+ /* Memory guarding is incompatible with super pages. */
+ for ( i = 0; i < (MAX_MONITOR_ADDRESS >> L2_PAGETABLE_SHIFT); i++ )
+ {
+ l1 = (l1_pgentry_t *)heap_start;
+ heap_start = (void *)((unsigned long)heap_start + PAGE_SIZE);
+ for ( j = 0; j < ENTRIES_PER_L1_PAGETABLE; j++ )
+ l1[j] = mk_l1_pgentry((i << L2_PAGETABLE_SHIFT) |
+ (j << L1_PAGETABLE_SHIFT) |
+ __PAGE_HYPERVISOR);
+ idle_pg_table[i] = idle_pg_table[i + l2_table_offset(PAGE_OFFSET)] =
+ mk_l2_pgentry(virt_to_phys(l1) | __PAGE_HYPERVISOR);
+ }
+
+ return heap_start;
+}
+
+static void __memguard_change_range(void *p, unsigned long l, int guard)
+{
+ l1_pgentry_t *l1;
+ l2_pgentry_t *l2;
+ unsigned long _p = (unsigned long)p;
+ unsigned long _l = (unsigned long)l;
+
+ /* Ensure we are dealing with a page-aligned whole number of pages. */
+ ASSERT((_p&PAGE_MASK) != 0);
+ ASSERT((_l&PAGE_MASK) != 0);
+ ASSERT((_p&~PAGE_MASK) == 0);
+ ASSERT((_l&~PAGE_MASK) == 0);
+
+ while ( _l != 0 )
+ {
+ l2 = &idle_pg_table[l2_table_offset(_p)];
+ l1 = l2_pgentry_to_l1(*l2) + l1_table_offset(_p);
+ if ( guard )
+ *l1 = mk_l1_pgentry(l1_pgentry_val(*l1) & ~_PAGE_PRESENT);
+ else
+ *l1 = mk_l1_pgentry(l1_pgentry_val(*l1) | _PAGE_PRESENT);
+ _p += PAGE_SIZE;
+ _l -= PAGE_SIZE;
+ }
+}
+
+void memguard_guard_range(void *p, unsigned long l)
+{
+ __memguard_change_range(p, l, 1);
+ local_flush_tlb();
+}
+
+void memguard_unguard_range(void *p, unsigned long l)
+{
+ __memguard_change_range(p, l, 0);
+}
+
+int memguard_is_guarded(void *p)
+{
+ l1_pgentry_t *l1;
+ l2_pgentry_t *l2;
+ unsigned long _p = (unsigned long)p;
+ l2 = &idle_pg_table[l2_table_offset(_p)];
+ l1 = l2_pgentry_to_l1(*l2) + l1_table_offset(_p);
+ return !(l1_pgentry_val(*l1) & _PAGE_PRESENT);
+}
+
+#endif
extern int do_timer_lists_from_pit;
unsigned long low_mem_size;
-#ifdef STACK_GUARD
- extern unsigned long cpu0_stack[];
- l1_pgentry_t *l1;
- l2_pgentry_t *l2;
- int i, j;
-
- /* When stack-guarding, Xen's heap cannot be mapped by super pages. */
- for ( i = 0; i < (MAX_MONITOR_ADDRESS >> L2_PAGETABLE_SHIFT); i++ )
- {
- l1 = (l1_pgentry_t *)get_free_page(GFP_KERNEL);
- for ( j = 0; j < ENTRIES_PER_L1_PAGETABLE; j++ )
- l1[j] = mk_l1_pgentry((i << L2_PAGETABLE_SHIFT) |
- (j << L1_PAGETABLE_SHIFT) |
- PAGE_HYPERVISOR);
- idle_pg_table[i] = idle_pg_table[i + l2_table_offset(PAGE_OFFSET)] =
- mk_l2_pgentry(virt_to_phys(l1) | PAGE_HYPERVISOR);
- }
-
/* Unmap the first page of CPU0's stack. */
- l2 = &idle_pg_table[l2_table_offset(virt_to_phys(cpu0_stack))];
- l1 = l2_pgentry_to_l1(*l2) + l1_table_offset(virt_to_phys(cpu0_stack));
- *l1 = mk_l1_pgentry(0);
-#endif
+ extern unsigned long cpu0_stack[];
+ memguard_guard_range(cpu0_stack, PAGE_SIZE);
if ( opt_watchdog )
nmi_watchdog = NMI_LOCAL_APIC;
printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip);
stack = __pa(__get_free_pages(GFP_KERNEL, 1));
-#ifdef STACK_GUARD
- {
- /* Unmap the first page of the new CPU0's stack. */
- l2_pgentry_t *l2 = &idle_pg_table[l2_table_offset(stack)];
- l1_pgentry_t *l1 = l2_pgentry_to_l1(*l2) + l1_table_offset(stack);
- *l1 = mk_l1_pgentry(0);
- }
-#endif
stack_start.esp = stack + STACK_SIZE - STACK_RESERVED;
+ /* Debug build: detect stack overflow by setting up a guard page. */
+ memguard_guard_range(__va(stack), PAGE_SIZE);
+
/*
* This grunge runs the startup process for
* the targeted processor.
page = ((unsigned long *) __va(page))[(addr&0x3ff000)>>PAGE_SHIFT];
printk(" *pte = %08lx\n", page);
}
+#ifdef MEMORY_GUARD
+ if ( !(error_code & 1) )
+ printk(" -- POSSIBLY AN ACCESS TO FREED MEMORY? --\n");
+#endif
}
show_registers(regs);
guest_event_notify(cpu_mask);
out:
- spin_unlock(&p1->event_channel_lock);
- put_task_struct(p1);
-
if ( p2 != NULL )
{
if ( p1 != p2 )
put_task_struct(p2);
}
+ spin_unlock(&p1->event_channel_lock);
+
return rc;
}
unsigned long max_page;
unsigned char *cmdline, *p;
module_t *mod;
+ void *heap_start;
int i;
/* Parse the command-line options. */
printk("Initialised all memory on a %luMB machine\n",
max_page >> (20-PAGE_SHIFT));
- init_page_allocator(__pa(&_end), MAX_MONITOR_ADDRESS);
+ heap_start = memguard_init(&_end);
+
+ init_page_allocator(__pa(heap_start), MAX_MONITOR_ADDRESS);
/* Initialise the slab allocator. */
kmem_cache_init();
}
-unsigned short compute_cksum(unsigned short *buf, int count)
-{
- unsigned long sum = 0;
- while ( count-- )
- sum += *buf++;
- while ( sum >> 16 )
- sum = (sum & 0xffff) + (sum >> 16);
- return (unsigned short) ~sum;
-}
-
-
-/*
- * Function written by ek247. Exports console output from all domains upwards
- * to domain0, by stuffing it into a fake network packet.
- */
-int console_export(char *str, int len)
-{
- struct sk_buff *skb;
- struct iphdr *iph = NULL;
- struct udphdr *udph = NULL;
- struct ethhdr *ethh = NULL;
- int hdr_size = sizeof(struct iphdr) + sizeof(struct udphdr);
- u8 *skb_data;
-
- skb = dev_alloc_skb(sizeof(struct ethhdr) +
- hdr_size + len + 20);
- if ( skb == NULL ) return 0;
-
- skb->dev = the_dev;
- skb_data = (u8 *)map_domain_mem((skb->pf - frame_table) << PAGE_SHIFT);
- skb_reserve(skb, 2);
-
- /* Get a pointer to each header. */
- ethh = (struct ethhdr *)
- (skb_data + (skb->data - skb->head));
- iph = (struct iphdr *)(ethh + 1);
- udph = (struct udphdr *)(iph + 1);
-
- skb_reserve(skb, sizeof(struct ethhdr));
- skb_put(skb, hdr_size + len);
-
- /* Build IP header. */
- iph->version = 4;
- iph->ihl = 5;
- iph->tos = 0;
- iph->tot_len = htons(hdr_size + len);
- iph->id = 0xdead;
- iph->frag_off= 0;
- iph->ttl = 255;
- iph->protocol= 17;
- iph->daddr = htonl(0xa9fe0100); /* 169.254.1.0 */
- iph->saddr = htonl(0xa9fefeff); /* 169.254.254.255 */
- iph->check = 0;
- iph->check = compute_cksum((__u16 *)iph, sizeof(struct iphdr)/2);
-
- /* Build UDP header. */
- udph->source = htons(current->domain);
- udph->dest = htons(666);
- udph->len = htons(sizeof(struct udphdr) + len);
- udph->check = 0;
-
- /* Build the UDP payload. */
- memcpy((char *)(udph + 1), str, len);
-
- /* Fix Ethernet header. */
- memset(ethh->h_source, 0, ETH_ALEN);
- memset(ethh->h_dest, 0, ETH_ALEN);
- ethh->h_proto = htons(ETH_P_IP);
- skb->mac.ethernet= (struct ethhdr *)ethh;
-
- unmap_domain_mem(skb_data);
-
- skb->dst_vif = find_net_vif(0, 0);
- (void)netif_rx(skb);
-
- return 1;
-}
-
-
long do_console_write(char *str, unsigned int count)
{
#define SIZEOF_BUF 256
__putstr(line_header);
__putstr(single_line);
spin_unlock_irqrestore(&console_lock, flags);
-
- if ( current->domain != 0 )
- console_export(single_line, j);
}
return 0;
/* No-op syscall. */
return -ENOSYS;
}
-
-
-/*
- * GRAVEYARD
- */
-#if 0
- if ( (mbi->flags & (1<<6)) )
- {
- memory_map_t *mmap = (memory_map_t *)mbi->mmap_addr;
- struct e820entry *e820 = E820_MAP;
-
- while ( (unsigned long)mmap < (mbi->mmap_addr + mbi->mmap_length) )
- {
- e820->addr_lo = mmap->base_addr_low;
- e820->addr_hi = mmap->base_addr_high;
- e820->size_lo = mmap->length_low;
- e820->size_hi = mmap->length_high;
- e820->type = mmap->type;
- e820++;
- mmap = (memory_map_t *)
- ((unsigned long)mmap + mmap->size + sizeof (mmap->size));
- }
- }
-#endif
-
#define FREELIST_SIZE ((sizeof(void*)<<3)-PAGE_SHIFT)
static chunk_head_t *free_head[FREELIST_SIZE];
static chunk_head_t free_tail[FREELIST_SIZE];
-#define FREELIST_EMPTY(_l) ((_l)->next == NULL)
+#define FREELIST_EMPTY(_i) (free_head[_i] == &free_tail[i])
#define round_pgdown(_p) ((_p)&PAGE_MASK)
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
+#ifdef MEMORY_GUARD
+
+/*
+ * Debug build: free memory chunks are made inaccessible.
+ */
+
+/* Make order-'o' pages inaccessible, from address 'p'. */
+static inline void GUARD(void *p, int o)
+{
+ p = (void *)((unsigned long)p&PAGE_MASK);
+ if ( p > (void *)&_end ) /* careful not to protect the 'free_tail' array */
+ memguard_guard_range(p, (1<<(o+PAGE_SHIFT)));
+}
+
+/* Make order-'o' pages accessible, from address 'p'. */
+static inline void UNGUARD(void *p, int o)
+{
+ p = (void *)((unsigned long)p&PAGE_MASK);
+ if ( p > (void *)&_end ) /* careful not to protect the 'free_tail' array */
+ memguard_unguard_range(p, (1<<(o+PAGE_SHIFT)));
+}
+
+/* Safe form of 'ch->level'. */
+static inline int HEAD_LEVEL(chunk_head_t *ch)
+{
+ int l;
+ ASSERT(memguard_is_guarded(ch));
+ UNGUARD(ch, 0);
+ l = ch->level;
+ GUARD(ch, 0);
+ return l;
+}
+
+/* Safe form of 'ct->level'. */
+static inline int TAIL_LEVEL(chunk_tail_t *ct)
+{
+ int l;
+ ASSERT(memguard_is_guarded(ct));
+ UNGUARD(ct, 0);
+ l = ct->level;
+ GUARD(ct, 0);
+ return l;
+}
+
+/* Safe form of '*ch->pprev = l'. */
+static inline void UPDATE_PREV_FORWARDLINK(chunk_head_t *ch, chunk_head_t *l)
+{
+ ASSERT(((void *)ch->pprev < (void *)&_end) ||
+ memguard_is_guarded(ch->pprev));
+ UNGUARD(ch->pprev, 0);
+ *ch->pprev = l;
+ GUARD(ch->pprev, 0);
+}
+
+/* Safe form of 'ch->next->pprev = l'. */
+static inline void UPDATE_NEXT_BACKLINK(chunk_head_t *ch, chunk_head_t **l)
+{
+ ASSERT(((void *)ch->next < (void *)&_end) ||
+ memguard_is_guarded(ch->next));
+ UNGUARD(ch->next, 0);
+ ch->next->pprev = l;
+ GUARD(ch->next, 0);
+}
+
+#else
+
+/*
+ * Non-debug build: free memory chunks are not made inaccessible.
+ */
+
+#define GUARD(_p,_o) ((void)0)
+#define UNGUARD(_p,_o) ((void)0)
+#define HEAD_LEVEL(_ch) ((_ch)->level)
+#define TAIL_LEVEL(_ct) ((_ct)->level)
+#define UPDATE_PREV_FORWARDLINK(_ch,_link) (*(_ch)->pprev = (_link))
+#define UPDATE_NEXT_BACKLINK(_ch,_link) ((_ch)->next->pprev = (_link))
+
+#endif
+
/*
* Initialise allocator, placing addresses [@min,@max] in free pool.
void __init init_page_allocator(unsigned long min, unsigned long max)
{
int i;
- unsigned long range, bitmap_size;
+ unsigned long range, bitmap_size, p, remaining;
chunk_head_t *ch;
chunk_tail_t *ct;
min += PAGE_OFFSET;
max += PAGE_OFFSET;
- while ( range != 0 )
+ p = min;
+ remaining = range;
+ while ( remaining != 0 )
{
/*
- * Next chunk is limited by alignment of min, but also
- * must not be bigger than remaining range.
+ * Next chunk is limited by alignment of p, but also must not be bigger
+ * than remaining bytes.
*/
- for ( i = PAGE_SHIFT; (1<<(i+1)) <= range; i++ )
- if ( min & (1<<i) ) break;
+ for ( i = PAGE_SHIFT; (1<<(i+1)) <= remaining; i++ )
+ if ( p & (1<<i) ) break;
- ch = (chunk_head_t *)min;
- min += (1<<i);
- range -= (1<<i);
- ct = (chunk_tail_t *)min-1;
+ ch = (chunk_head_t *)p;
+ p += (1<<i);
+ remaining -= (1<<i);
+ ct = (chunk_tail_t *)p - 1;
i -= PAGE_SHIFT;
ch->level = i;
ch->next = free_head[i];
free_head[i] = ch;
ct->level = i;
}
-}
-
-/* Release a PHYSICAL address range to the allocator. */
-void release_bytes_to_allocator(unsigned long min, unsigned long max)
-{
- min = round_pgup (min);
- max = round_pgdown(max);
-
- while ( min < max )
- {
- __free_pages(min+PAGE_OFFSET, 0);
- min += PAGE_SIZE;
- }
+ memguard_guard_range((void *)min, range);
}
/* Find smallest order which can satisfy the request. */
for ( i = order; i < FREELIST_SIZE; i++ ) {
- if ( !FREELIST_EMPTY(free_head[i]) )
+ if ( !FREELIST_EMPTY(i) )
break;
}
/* Unlink a chunk. */
alloc_ch = free_head[i];
+ UNGUARD(alloc_ch, i);
free_head[i] = alloc_ch->next;
- alloc_ch->next->pprev = alloc_ch->pprev;
+ /* alloc_ch->next->pprev = alloc_ch->pprev */
+ UPDATE_NEXT_BACKLINK(alloc_ch, alloc_ch->pprev);
/* We may have to break the chunk a number of times. */
while ( i != order )
spare_ct->level = i;
/* Link in the spare chunk. */
- spare_ch->next->pprev = &spare_ch->next;
+ /* spare_ch->next->pprev = &spare_ch->next */
+ UPDATE_NEXT_BACKLINK(spare_ch, &spare_ch->next);
free_head[i] = spare_ch;
+ GUARD(spare_ch, i);
}
map_alloc(__pa(alloc_ch)>>PAGE_SHIFT, 1<<order);
spin_unlock_irqrestore(&alloc_lock, flags);
+#ifdef MEMORY_GUARD
+ /* Now we blast the contents of the block. */
+ memset(alloc_ch, 0x55, 1 << (order + PAGE_SHIFT));
+#endif
+
return((unsigned long)alloc_ch);
no_memory:
spin_lock_irqsave(&alloc_lock, flags);
+#ifdef MEMORY_GUARD
+ /* Check that the block isn't already freed. */
+ if ( !allocated_in_map(pagenr) )
+ BUG();
+ /* Check that the block isn't already guarded. */
+ if ( __put_user(1, (int*)p) )
+ BUG();
+ /* Now we blast the contents of the block. */
+ memset((void *)p, 0xaa, size);
+#endif
+
map_free(pagenr, 1<<order);
/* Merge chunks as far as possible. */
if ( (p & size) )
{
/* Merge with predecessor block? */
- if ( allocated_in_map(pagenr-1) ) break;
+ if ( allocated_in_map(pagenr-1) )
+ break;
ct = (chunk_tail_t *)p - 1;
- if ( ct->level != order ) break;
+ if ( TAIL_LEVEL(ct) != order )
+ break;
ch = (chunk_head_t *)(p - size);
p -= size;
}
else
{
/* Merge with successor block? */
- if ( allocated_in_map(pagenr+(1<<order)) ) break;
+ if ( allocated_in_map(pagenr+(1<<order)) )
+ break;
ch = (chunk_head_t *)(p + size);
- if ( ch->level != order ) break;
+ if ( HEAD_LEVEL(ch) != order )
+ break;
}
/* Okay, unlink the neighbour. */
- *ch->pprev = ch->next;
- ch->next->pprev = ch->pprev;
+ UNGUARD(ch, order);
+ /* *ch->pprev = ch->next */
+ UPDATE_PREV_FORWARDLINK(ch, ch->next);
+ /* ch->next->pprev = ch->pprev */
+ UPDATE_NEXT_BACKLINK(ch, ch->pprev);
order++;
size <<= 1;
ch->level = order;
ch->pprev = &free_head[order];
ch->next = free_head[order];
- ch->next->pprev = &ch->next;
+ /* ch->next->pprev = &ch->next */
+ UPDATE_NEXT_BACKLINK(ch, &ch->next);
free_head[order] = ch;
+ GUARD(ch, order);
spin_unlock_irqrestore(&alloc_lock, flags);
}
do {
if ( (x = y) == TASK_DYING ) return 0;
} while ( (y = cmpxchg(&p->state, x, TASK_DYING)) != x );
+ rem_ac_timer(&p->timer);
return 1;
}
#ifndef NDEBUG
#define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \
__FILE__ , __LINE__ , ## _a )
-#define STACK_GUARD
+#define MEMORY_GUARD
#define TRACE_BUFFER
#else
#define DPRINTK(_f, _a...) ((void)0)
*/
void init_page_allocator(unsigned long min, unsigned long max);
-void release_bytes_to_allocator(unsigned long min, unsigned long max);
unsigned long __get_free_pages(int mask, int order);
void __free_pages(unsigned long p, int order);
#define get_free_page(_m) (__get_free_pages((_m),0))
#define DEFAULT_GDT_ENTRIES ((LAST_RESERVED_GDT_ENTRY*8)+7)
#define DEFAULT_GDT_ADDRESS ((unsigned long)gdt_table)
+#ifdef MEMORY_GUARD
+void *memguard_init(void *heap_start);
+void memguard_guard_range(void *p, unsigned long l);
+void memguard_unguard_range(void *p, unsigned long l);
+int memguard_is_guarded(void *p);
+#else
+#define memguard_init(_s) (_s)
+#define memguard_guard_range(_p,_l) ((void)0)
+#define memguard_unguard_range(_p,_l) ((void)0)
+#define memguard_is_guarded(_p) (0)
+#endif
+
#endif /* __XENO_MM_H__ */
endif
CORE_FILES += arch/xeno/kernel/kernel.o arch/xeno/mm/mm.o
+CORE_FILES += arch/xeno/drivers/evtchn/evtchn.o
CORE_FILES += arch/xeno/drivers/console/con.o
CORE_FILES += arch/xeno/drivers/block/blk.o
-CORE_FILES += arch/xeno/drivers/evtchn/evtchn.o
CORE_FILES += arch/xeno/drivers/network/net.o
ifdef CONFIG_XENO_PRIV
CORE_FILES += arch/xeno/drivers/dom0/dom0.o
/******************************************************************************
* console.c
+ *
+ * Virtual console driver.
+ *
+ * Copyright (c) 2002-2004, K A Fraser.
*/
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/console.h>
-
+#include <asm/evtchn.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/hypervisor.h>
+#include <asm/hypervisor-ifs/event_channel.h>
+#include <asm/control_if.h>
+
+static spinlock_t xeno_console_lock = SPIN_LOCK_UNLOCKED;
#define XENO_TTY_MINOR 123
-/*** Useful function for console debugging -- goes straight to Xen ****/
-asmlinkage int xprintk(const char *fmt, ...)
+/******************** Kernel console driver ********************************/
+
+static void nonpriv_conwrite(const char *s, unsigned int count)
{
- va_list args;
- int printk_len;
- static char printk_buf[1024];
-
- /* Emit the output into the temporary buffer */
- va_start(args, fmt);
- printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
- va_end(args);
-
- /* Send the processed output directly to Xen. */
- (void)HYPERVISOR_console_write(printk_buf, printk_len);
+ control_if_t *ctrl_if;
+ evtchn_op_t evtchn_op;
+ int src, dst, p;
+ unsigned long flags;
- return 0;
-}
+ ctrl_if = (control_if_t *)((char *)HYPERVISOR_shared_info + 2048);
+ spin_lock_irqsave(&xeno_console_lock, flags);
+ while ( count != 0 )
+ {
+ /* Wait for the request ring to drain. */
+ while ( ctrl_if->tx_resp_prod != ctrl_if->tx_req_prod )
+ barrier();
+
+ p = MASK_CONTROL_IDX(ctrl_if->tx_req_prod);
+
+ ctrl_if->tx_ring[p].cmd_type = CMD_CONSOLE;
+ ctrl_if->tx_ring[p].cmd_subtype = CMD_CONSOLE_DATA;
+ ctrl_if->tx_ring[p].id = 0xaa;
+ src = dst = 0;
+ while ( (src < count) && (dst < (sizeof(ctrl_if->tx_ring[p].msg)-1)) )
+ {
+ if ( (ctrl_if->tx_ring[p].msg[dst++] = s[src++]) == '\n' )
+ ctrl_if->tx_ring[p].msg[dst++] = '\r';
+ }
+ ctrl_if->tx_ring[p].length = dst;
+
+ ctrl_if->tx_req_prod++;
+ evtchn_op.cmd = EVTCHNOP_send;
+ evtchn_op.u.send.local_port = 0;
+ (void)HYPERVISOR_event_channel_op(&evtchn_op);
+
+ s += src;
+ count -= src;
+ }
-/******************** Kernel console driver ********************************/
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
+}
-static void xen_console_write(struct console *co, const char *s, unsigned count)
+static void priv_conwrite(const char *s, unsigned int count)
{
-#define STRLEN 256
- static char str[STRLEN];
+ static char str[256];
static int pos = 0;
int len;
-
+
/* We buffer output until we see a newline, or until the buffer is full. */
while ( count != 0 )
{
- len = ((STRLEN - pos) > count) ? count : STRLEN - pos;
+ len = ((sizeof(str) - pos) > count) ? count : sizeof(str) - pos;
memcpy(str + pos, s, len);
pos += len;
s += len;
count -= len;
- if ( (pos == STRLEN) || (str[pos-1] == '\n') )
+ if ( (pos == sizeof(str)) || (str[pos-1] == '\n') )
{
(void)HYPERVISOR_console_write(str, pos);
pos = 0;
}
}
+static void xen_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ if ( !(start_info.flags & SIF_INITDOMAIN) )
+ nonpriv_conwrite(s, count);
+ else
+ priv_conwrite(s, count);
+}
+
static kdev_t xen_console_device(struct console *c)
{
/*
void xen_console_init(void)
{
- xprintk("xen_console_init\n");
- register_console(&xen_console_info);
+ register_console(&xen_console_info);
}
-/******************** Initial /dev/console *********************************/
+/*** Useful function for console debugging -- goes straight to Xen ****/
+asmlinkage int xprintk(const char *fmt, ...)
+{
+ va_list args;
+ int printk_len;
+ static char printk_buf[1024];
+
+ /* Emit the output into the temporary buffer */
+ va_start(args, fmt);
+ printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
+ va_end(args);
+
+ /* Send the processed output directly to Xen. */
+ xen_console_write(NULL, printk_buf, printk_len);
+
+ return 0;
+}
+
+/******************** User-space console driver (/dev/console) ************/
static struct tty_driver xeno_console_driver;
static int xeno_console_refcount;
static struct tty_struct *xeno_console_table[1];
static struct termios *xeno_console_termios[1];
static struct termios *xeno_console_termios_locked[1];
+static struct tty_struct *xeno_console_tty;
+static int xeno_console_use_count;
+
+#define WBUF_SIZE 1024
+#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
+static char wbuf[WBUF_SIZE], x_char;
+static unsigned int wc, wp; /* write_cons, write_prod */
+
+static void __do_console_io(void)
+{
+ control_if_t *ctrl_if;
+ control_msg_t *msg;
+ evtchn_op_t evtchn_op;
+ CONTROL_RING_IDX c;
+ int i, len, work_done = 0;
+
+ if ( (start_info.flags & SIF_INITDOMAIN) || (xeno_console_tty == NULL) )
+ return;
+
+ /* Acknowledge the notification. */
+ evtchn_clear_port(0);
+
+ ctrl_if = (control_if_t *)((char *)HYPERVISOR_shared_info + 2048);
+
+ /* Receive work. */
+ for ( c = ctrl_if->rx_resp_prod; c != ctrl_if->rx_req_prod; c++ )
+ {
+ msg = &ctrl_if->rx_ring[MASK_CONTROL_IDX(c)];
+ if ( (msg->cmd_type == CMD_CONSOLE) &&
+ (msg->cmd_subtype == CMD_CONSOLE_DATA) )
+ {
+ for ( i = 0; i < msg->length; i++ )
+ tty_insert_flip_char(xeno_console_tty, msg->msg[i], 0);
+ }
+ msg->length = 0;
+ }
+ if ( ctrl_if->rx_resp_prod != c )
+ {
+ ctrl_if->rx_resp_prod = c;
+ work_done = 1;
+ tty_flip_buffer_push(xeno_console_tty);
+ }
+
+ /* Transmit work. */
+ for ( c = ctrl_if->tx_req_prod;
+ (c - ctrl_if->tx_resp_prod) != CONTROL_RING_SIZE;
+ c++ )
+ {
+ if ( (wc == wp) && (x_char == 0) )
+ break;
+ msg = &ctrl_if->tx_ring[MASK_CONTROL_IDX(c)];
+ msg->cmd_type = CMD_CONSOLE;
+ msg->cmd_subtype = CMD_CONSOLE_DATA;
+ msg->id = 0xaa;
+ len = 0;
+ if ( x_char != 0 ) /* Handle XON/XOFF urgently. */
+ {
+ msg->msg[len++] = x_char;
+ x_char = 0;
+ }
+ while ( (len < sizeof(msg->msg)) && (wc != wp) )
+ msg->msg[len++] = wbuf[WBUF_MASK(wc++)];
+ msg->length = len;
+ }
+ if ( ctrl_if->tx_req_prod != c )
+ {
+ ctrl_if->tx_req_prod = c;
+ work_done = 1;
+ }
+
+ if ( work_done )
+ {
+ /* Send a notification to the controller. */
+ evtchn_op.cmd = EVTCHNOP_send;
+ evtchn_op.u.send.local_port = 0;
+ (void)HYPERVISOR_event_channel_op(&evtchn_op);
+
+ /* There might be something for waiters to do. */
+ if ( xeno_console_tty != NULL )
+ wake_up_interruptible(&xeno_console_tty->write_wait);
+ }
+}
+
+static void control_event(unsigned int port)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&xeno_console_lock, flags);
+ __do_console_io();
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
+}
static int xeno_console_write_room(struct tty_struct *tty)
{
- return INT_MAX;
+ return WBUF_SIZE - (wp - wc);
}
static int xeno_console_chars_in_buffer(struct tty_struct *tty)
{
- return 0;
+ return wp - wc;
+}
+
+static void xeno_console_send_xchar(struct tty_struct *tty, char ch)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&xeno_console_lock, flags);
+ x_char = ch;
+ __do_console_io();
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
+}
+
+static void xeno_console_throttle(struct tty_struct *tty)
+{
+ if ( I_IXOFF(tty) )
+ xeno_console_send_xchar(tty, STOP_CHAR(tty));
+}
+
+static void xeno_console_unthrottle(struct tty_struct *tty)
+{
+ if ( I_IXOFF(tty) )
+ {
+ if ( x_char != 0 )
+ x_char = 0;
+ else
+ xeno_console_send_xchar(tty, START_CHAR(tty));
+ }
+}
+
+static void xeno_console_flush_buffer(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&xeno_console_lock, flags);
+ wc = wp = 0;
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
}
-static inline int xeno_console_xmit(int ch)
+static inline int __xeno_console_put_char(int ch)
{
- char _ch = ch;
- xen_console_write(NULL, &_ch, 1);
+ char _ch = (char)ch;
+
+ if ( start_info.flags & SIF_INITDOMAIN )
+ {
+ priv_conwrite(&_ch, 1);
+ return 1;
+ }
+
+ if ( (wp - wc) == WBUF_SIZE )
+ return 0;
+ wbuf[WBUF_MASK(wp++)] = _ch;
return 1;
}
const u_char * buf, int count)
{
int i;
+ unsigned long flags;
if ( from_user && verify_area(VERIFY_READ, buf, count) )
- {
return -EINVAL;
- }
+
+ spin_lock_irqsave(&xeno_console_lock, flags);
for ( i = 0; i < count; i++ )
{
char ch;
if ( from_user )
- {
__get_user(ch, buf + i);
- }
else
- {
ch = buf[i];
- }
- xeno_console_xmit(ch);
+ if ( !__xeno_console_put_char(ch) )
+ break;
}
+
+ if ( i != 0 )
+ __do_console_io();
+
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
+
return i;
}
static void xeno_console_put_char(struct tty_struct *tty, u_char ch)
{
- xeno_console_xmit(ch);
+ unsigned long flags;
+ spin_lock_irqsave(&xeno_console_lock, flags);
+ (void)__xeno_console_put_char(ch);
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
+}
+
+static void xeno_console_flush_chars(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&xeno_console_lock, flags);
+
+ __do_console_io();
+ spin_unlock_irqrestore(&xeno_console_lock, flags);
}
static int xeno_console_open(struct tty_struct *tty, struct file *filp)
}
tty->driver_data = NULL;
+ if ( xeno_console_tty == NULL )
+ {
+ xeno_console_tty = tty;
+ wc = wp = 0;
+ __do_console_io();
+ }
+
+ xeno_console_use_count++;
return 0;
}
static void xeno_console_close(struct tty_struct *tty, struct file *filp)
{
+ if ( --xeno_console_use_count == 0 )
+ xeno_console_tty = NULL;
MOD_DEC_USE_COUNT;
}
{
memset(&xeno_console_driver, 0, sizeof(struct tty_driver));
xeno_console_driver.magic = TTY_DRIVER_MAGIC;
- xeno_console_driver.driver_name = "xeno_console";
- xeno_console_driver.name = "xencon";
+ xeno_console_driver.name = "xencons";
xeno_console_driver.major = TTY_MAJOR;
xeno_console_driver.minor_start = XENO_TTY_MINOR;
xeno_console_driver.num = 1;
xeno_console_driver.type = TTY_DRIVER_TYPE_SERIAL;
xeno_console_driver.subtype = SERIAL_TYPE_NORMAL;
xeno_console_driver.init_termios = tty_std_termios;
- xeno_console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ xeno_console_driver.flags = TTY_DRIVER_REAL_RAW;
xeno_console_driver.refcount = &xeno_console_refcount;
xeno_console_driver.table = xeno_console_table;
xeno_console_driver.termios = xeno_console_termios;
xeno_console_driver.termios_locked = xeno_console_termios_locked;
- /* Functions */
+
xeno_console_driver.open = xeno_console_open;
xeno_console_driver.close = xeno_console_close;
xeno_console_driver.write = xeno_console_write;
xeno_console_driver.write_room = xeno_console_write_room;
xeno_console_driver.put_char = xeno_console_put_char;
+ xeno_console_driver.flush_chars = xeno_console_flush_chars;
xeno_console_driver.chars_in_buffer = xeno_console_chars_in_buffer;
+ xeno_console_driver.send_xchar = xeno_console_send_xchar;
+ xeno_console_driver.flush_buffer = xeno_console_flush_buffer;
+ xeno_console_driver.throttle = xeno_console_throttle;
+ xeno_console_driver.unthrottle = xeno_console_unthrottle;
if ( tty_register_driver(&xeno_console_driver) )
+ panic("Couldn't register Xeno console driver\n");
+
+ if ( !(start_info.flags & SIF_INITDOMAIN) )
{
- printk(KERN_ERR "Couldn't register Xeno console driver\n");
- }
- else
- {
- printk("Xeno console successfully installed\n");
+ if ( evtchn_request_port(0, control_event) != 0 )
+ BUG();
+ /* Kickstart event delivery. */
+ control_event(0);
}
+ printk("Xeno console successfully installed\n");
+
return 0;
}
ret = tty_unregister_driver(&xeno_console_driver);
if ( ret != 0 )
- {
printk(KERN_ERR "Unable to unregister Xeno console driver: %d\n", ret);
- }
+
+ if ( !(start_info.flags & SIF_INITDOMAIN) )
+ (void)evtchn_free_port(0);
}
module_init(xeno_con_init);
#include <linux/devfs_fs_kernel.h>
#include <linux/stat.h>
#include <linux/poll.h>
-
-typedef void (*evtchn_receiver_t)(unsigned int);
-#define PORT_NORMAL 0x0000
-#define PORT_DISCONNECT 0x8000
-#define PORTIDX_MASK 0x7fff
-
-/* /dev/xen/evtchn resides at device number major=10, minor=200 */
-#define EVTCHN_MINOR 200
-
-/* /dev/xen/evtchn ioctls: */
-/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
-#define EVTCHN_RESET _IO('E', 1)
+#include <asm/evtchn.h>
/* NB. This must be shared amongst drivers if more things go in /dev/xen */
static devfs_handle_t xen_dev_dir;
void evtchn_clear_port(unsigned int port)
{
unsigned int p = port & PORTIDX_MASK;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lock, flags);
+
if ( unlikely(port & PORT_DISCONNECT) )
{
- clear_bit(p, &HYPERVISOR_shared_info->event_channel_disc[0]);
- wmb(); /* clear the source first, then our quenchmask */
clear_bit(p, &disc_outstanding[0]);
+ clear_bit(p, &HYPERVISOR_shared_info->event_channel_disc[0]);
}
else
{
- clear_bit(p, &HYPERVISOR_shared_info->event_channel_pend[0]);
- wmb(); /* clear the source first, then our quenchmask */
clear_bit(p, &pend_outstanding[0]);
+ clear_bit(p, &HYPERVISOR_shared_info->event_channel_pend[0]);
}
+
+ spin_unlock_irqrestore(&lock, flags);
}
static inline void process_bitmask(u32 *sel,
}
}
}
-
}
static void evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
schedule();
}
- rc = -EFAULT;
-
- /* Byte length of first chunk. May be truncated by ring wrap. */
+ /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
if ( ((c ^ p) & RING_SIZE) != 0 )
+ {
bytes1 = (RING_SIZE - RING_MASK(c)) * sizeof(u16);
+ bytes2 = RING_MASK(p) * sizeof(u16);
+ }
else
+ {
bytes1 = (p - c) * sizeof(u16);
+ bytes2 = 0;
+ }
- /* Further truncate chunk length according to caller's maximum count. */
+ /* Truncate chunks according to caller's maximum byte count. */
if ( bytes1 > count )
+ {
bytes1 = count;
+ bytes2 = 0;
+ }
+ else if ( (bytes1 + bytes2) > count )
+ {
+ bytes2 = count - bytes1;
+ }
- /* Copy the first chunk. */
- if ( copy_to_user(buf, &ring[c], bytes1) != 0 )
- goto out;
-
- /* More bytes to copy? */
- if ( count > bytes1 )
+ if ( copy_to_user(buf, &ring[RING_MASK(c)], bytes1) ||
+ ((bytes2 != 0) && copy_to_user(&buf[bytes1], &ring[0], bytes2)) )
{
- bytes2 = RING_MASK(p) * sizeof(u16);
- if ( bytes2 > count )
- bytes2 = count;
- if ( (bytes2 != 0) && copy_to_user(&buf[bytes1], &ring[0], bytes2) )
- goto out;
+ rc = -EFAULT;
+ goto out;
}
- ring_cons = (bytes1 + bytes2) / sizeof(u16);
+ ring_cons += (bytes1 + bytes2) / sizeof(u16);
rc = bytes1 + bytes2;
return err;
}
+ /* Kickstart servicing of notifications. */
+ evtchn_interrupt(0, NULL, NULL);
+
+ printk("Event-channel driver installed.\n");
+
return 0;
}
if ( (timer = next_timer_event()) != NULL )
alarm = __jiffies_to_st(timer->expires);
+ /* Tasks on the timer task queue expect to be executed on the next tick. */
+ if ( TQ_ACTIVE(tq_timer) )
+ alarm = __jiffies_to_st(jiffies + 1);
+
/* Failure is pretty bad, but we'd best soldier on. */
if ( HYPERVISOR_set_timer_op(alarm) != 0 )
ret = -1;
--- /dev/null
+/******************************************************************************
+ * control_if.h
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __CONTROL_IF_H__
+#define __CONTROL_IF_H__
+
+typedef struct {
+ u8 cmd_type; /* echoed in response */
+ u8 cmd_subtype; /* echoed in response */
+ u8 id; /* echoed in response */
+ u8 length; /* number of bytes in 'msg' */
+ unsigned char msg[60]; /* command-specific message data */
+} control_msg_t;
+
+#define CONTROL_RING_SIZE 8
+typedef unsigned int CONTROL_RING_IDX;
+#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1))
+
+typedef struct {
+ control_msg_t tx_ring[CONTROL_RING_SIZE]; /* guest-OS -> controller */
+ control_msg_t rx_ring[CONTROL_RING_SIZE]; /* controller -> guest-OS */
+ CONTROL_RING_IDX tx_req_prod, tx_resp_prod;
+ CONTROL_RING_IDX rx_req_prod, rx_resp_prod;
+} control_if_t;
+
+#define CMD_CONSOLE 0
+#define CMD_CONSOLE_DATA 0
+
+#endif /* __CONTROL_IF_H__ */
--- /dev/null
+/******************************************************************************
+ * evtchn.h
+ *
+ * Driver for receiving and demuxing event-channel signals.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __ASM_EVTCHN_H__
+#define __ASM_EVTCHN_H__
+
+typedef void (*evtchn_receiver_t)(unsigned int);
+#define PORT_NORMAL 0x0000
+#define PORT_DISCONNECT 0x8000
+#define PORTIDX_MASK 0x7fff
+
+/* /dev/xen/evtchn resides at device number major=10, minor=200 */
+#define EVTCHN_MINOR 200
+
+/* /dev/xen/evtchn ioctls: */
+/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
+#define EVTCHN_RESET _IO('E', 1)
+
+int evtchn_request_port(unsigned int port, evtchn_receiver_t rx_fn);
+int evtchn_free_port(unsigned int port);
+void evtchn_clear_port(unsigned int port);
+
+
+#endif /* __ASM_EVTCHN_H__ */
return ret;
}
+static inline int HYPERVISOR_event_channel_op(void *op)
+{
+ int ret;
+ __asm__ __volatile__ (
+ TRAP_INSTR
+ : "=a" (ret) : "0" (__HYPERVISOR_event_channel_op),
+ "b" (op) : "memory" );
+
+ return ret;
+}
+
static inline int HYPERVISOR_xen_version(int cmd)
{
int ret;